﻿using System;
using GodProject.peoples;

namespace GodProject.generators
{
  /// <summary>
  /// Generates peoples accordingly to god's rules
  /// </summary>
  public class GodPeopleGenerator
  {

    private IPeoplesRandomizer randomizer = new SimplePeoplesRandomizer();

    #region public api
    /// <summary>
    /// Creates random human
    /// </summary>
    /// <param name="sex">sex of human to be created</param>
    /// <returns>new random human</returns>
    public Human CreateHuman(Sex sex)
    {
      HumanType type = randomizer.GenerateRandomType(sex);
      return CreateHuman(sex, type);
    }

    /// <summary>
    /// Creates random human
    /// </summary>
    /// <returns>new random human</returns>
    public Human CreateHuman()
    {
      Sex sex = randomizer.GenerateRandomSex();
      return CreateHuman(sex);
    }

    /// <summary>
    /// Creates random human with pertient sex and type
    /// </summary>
    /// <param name="sex">human's sex</param>
    /// <param name="type">human's type</param>
    /// <returns>new human</returns>
    public Human CreateHuman(Sex sex, HumanType type)
    {
      switch (type)
      {
        case HumanType.Botan:
        return CreateBotan(sex);
        case HumanType.Student:
        return CreateStudent(sex);
        case HumanType.Parent:
        return CreateParent();
        default:
        return CreateCoolParent();
      }
    }

    /// <summary>
    /// Creates pair for human
    /// </summary>
    /// <param name="human">human, for witch pair will be created</param>
    /// <returns>new human (pair for specified human)</returns>
    public Human CreatePair(Human human)
    {
      Human pair;
      if (human is Botan)
      {
        Botan botan = human as Botan;
        pair = CreateCoolParent(botan);
      }
      else if (human is Student)
      {
        Student student = human as Student;
        pair = CreateParent(student);
      }
      else if (human is CoolParent)
      {
        CoolParent coolParent = human as CoolParent;
        pair = CreateBotan(coolParent);
      }
      else
      {
        Parent parent = human as Parent;
        pair = CreateStudent(parent);
      }
      return pair;
    }
    #endregion public api

    #region Student creation

    /// <summary>
    /// Creates pair Student for Parent
    /// </summary>
    /// <param name="parent">parent, for which student will be created</param>
    /// <returns>new student or null</returns>
    private Student CreateStudent(Parent parent)
    {
      Sex sex = randomizer.GenerateRandomSex();
      string middlename = NamesBuilder.CreateStudentSurnameByParentName(sex, parent.Name);
      return CreateStudent(sex, middlename);
    }

    /// <summary>
    /// Creates random student
    /// </summary>
    /// <param name="sex">sex of student to be created</param>
    /// <returns>new random student</returns>
    private Student CreateStudent(Sex sex)
    {
      string middlename = NamesBuilder.GenerateSurname(sex);
      return CreateStudent(sex, middlename);
    }

    /// <summary>
    /// Creates student by sex and middlename
    /// </summary>
    /// <param name="sex">sex of student</param>
    /// <param name="middlename">middlename of student</param>
    /// <returns>created student</returns>
    private Student CreateStudent(Sex sex, string middlename)
    {
      string name = NamesBuilder.GenerateName(sex);
      int age = randomizer.CalculateRandomStudentAge();
      return new Student(name, sex, age, middlename);
    }
    #endregion Student creation

    #region Botan creation
    /// <summary>
    /// Creates pair Botan for Cool Parent
    /// </summary>
    /// <param name="parent">cool parent, for which botan will be created</param>
    /// <returns>new botan or null</returns>
    private Botan CreateBotan(CoolParent parent)
    {
      Sex sex = randomizer.GenerateRandomSex();
      string middlename = NamesBuilder.CreateStudentSurnameByParentName(sex, parent.Name);
      double avg = CalculateAvgMarksByMoney(parent.Money);
      return CreateBotan(sex, middlename, avg);
    }

    /// <summary>
    /// Creates random botan
    /// </summary>
    /// <param name="sex">sex of botan to be created</param>
    /// <returns>new random botan</returns>
    private Botan CreateBotan(Sex sex)
    {
      string middlename = NamesBuilder.GenerateSurname(sex);
      double avgMarks = randomizer.CalculateRandomAvgMarks();
      return CreateBotan(sex, middlename, avgMarks);
    }

    /// <summary>
    /// Creates new botan by sex, middlename and average marks count
    /// </summary>
    /// <param name="sex">sex of created botan</param>
    /// <param name="middlename">middlename of created botan</param>
    /// <param name="avgMarks">average maeks of created botan</param>
    /// <returns>created botan</returns>
    private Botan CreateBotan(Sex sex, string middlename, double avgMarks)
    {
      int studentAge = randomizer.CalculateRandomStudentAge();
      string name = NamesBuilder.GenerateName(sex);
      return new Botan(name, sex, studentAge, middlename, avgMarks);
    }
    #endregion Botan creation

    #region Parent creation

    /// <summary>
    /// Creates pair Parent by Student
    /// </summary>
    /// <param name="student">student, for which parent must be created</param>
    /// <returns>created parent or null</returns>
    private Parent CreateParent(Student student)
    {
      string name = NamesBuilder.CreateParentNameByStudentSurname(student.Middlename);
      return CreateParent(name);
    }

    /// <summary>
    /// Creates parent
    /// </summary>
    /// <returns>new random parent</returns>
    private Parent CreateParent()
    {
      string name = NamesBuilder.GenerateName(Sex.Male);

      return CreateParent(name);
    }

    /// <summary>
    /// Creates parent by name
    /// </summary>
    /// <param name="name">parent's name</param>
    /// <returns>new parent</returns>
    private Parent CreateParent(string name)
    {
      int age = randomizer.CalculateRandomParentAge();
      int childCount = randomizer.CalculateRandomChildCount();
      return new Parent(name, age, childCount);
    }

    #endregion Parent creation

    #region CoolParent creation

    /// <summary>
    /// Creates pair Cool Parent by Botan
    /// </summary>
    /// <param name="botan">botan, for which parent will be created</param>
    /// <returns>created cool parent or null</returns>
    private CoolParent CreateCoolParent(Botan botan)
    {
      string name = NamesBuilder.CreateParentNameByStudentSurname(botan.Middlename);
      double money = CalculateMoneyByAvgMarks(botan.AverageMark);
      return CreateCoolParent(name, money);
    }
    /// <summary>
    /// Creates cool parent
    /// </summary>
    /// <returns>new random cool parent</returns>
    private CoolParent CreateCoolParent()
    {
      string name = NamesBuilder.GenerateName(Sex.Male);
      double money = randomizer.CalculateRandomMoney();
      return CreateCoolParent(name, money);
    }

    /// <summary>
    /// Creates cool parent by name and money count
    /// </summary>
    /// <param name="name">cool parent's name</param>
    /// <param name="money">money count</param>
    /// <returns>created cool parent</returns>
    private CoolParent CreateCoolParent(string name, Double money)
    {
      int age = randomizer.CalculateRandomParentAge();
      int childCount = randomizer.CalculateRandomChildCount();
      return new CoolParent(name, age, childCount, money);
    }
    #endregion CoolParent creation

    #region additional methods
    /// <summary>
    /// Calculates money  by average mark
    /// </summary>
    /// <param name="avgMarks">average mark</param>
    /// <returns>money count</returns>
    private double CalculateMoneyByAvgMarks(double avgMarks)
    {
      return Math.Pow(10, avgMarks);
    }

    /// <summary>
    /// Calculates average mark by money
    /// </summary>
    /// <param name="money">money count</param>
    /// <returns>average mark</returns>
    private double CalculateAvgMarksByMoney(double money)
    {
      return Math.Log10(money);
    }
    #endregion additional methods

  }
}
